//////////////////////////////////////////////////////////////////////////
/// Copyright (c) 2011, Sidorenko 'PC' Alexander                       ///
/// mailto: sidorenko /*dot*/ alexander (at) gmail [dot] com           ///
/// This file is a part of the Radiating Gas Dynamics project          ///
//////////////////////////////////////////////////////////////////////////

#ifndef mathVec3_INCLUDED
#define mathVec3_INCLUDED

template<unsigned int N>
class VecN
{
public:

	VecN() {}

	VecN<N> operator +(const VecN<N>& second) const;
	VecN<N> operator -(const VecN<N>& second) const;

	VecN<N> operator -() const;

	VecN<N> operator *(gdScalar scalar) const;
	VecN<N> operator /(gdScalar scalar) const;

	gdScalar dotProduct(const VecN<N>& second) const;

	VecN<N>& operator +=(const VecN<N>& second);
	VecN<N>& operator -=(const VecN<N>& second);

	VecN<N>& operator *=(gdScalar scalar);
	VecN<N>& operator /=(gdScalar scalar);

	gdScalar lengthSquared() const;
	gdScalar length() const;

	gdScalar distance(const VecN<N> &second) const;
	gdScalar distanceSquared(const VecN<N> &second) const;

	void normalize();
	void setLength(gdScalar newLength);

	const gdScalar& operator [](unsigned int index) const;
	gdScalar& operator [](unsigned int index);

	void set(gdScalar val);

	gdScalar data[N];

};

template<unsigned int N>
VecN<N> Min(const VecN<N>& a, const VecN<N>& b)
{
	VecN<N> r;
	for (unsigned int i = 0; i < N; ++i)
		r.data[i] = min(a.data[i], b.data[i]);
	return r;
}

template<unsigned int N>
VecN<N> Max(const VecN<N>& a, const VecN<N>& b)
{
	VecN<N> r;
	for (unsigned int i = 0; i < N; ++i)
		r.data[i] = max(a.data[i], b.data[i]);
	return r;
}

// Implementation

//////////////////////////////////////////////////////////////////////////
template<unsigned int N>
VecN<N> VecN<N>::operator +(const VecN<N>& second) const
{
	VecN<N> res;
	for (unsigned int i = 0; i < N; ++i)
	{
		res.data[i] = data[i] + second.data[i];
	}
	return res;
}

//////////////////////////////////////////////////////////////////////////
template<unsigned int N>
VecN<N> VecN<N>::operator -(const VecN<N>& second) const
{
	VecN<N> res;
	for (unsigned int i = 0; i < N; ++i)
	{
		res.data[i] = data[i] - second.data[i];
	}
	return res;
}

//////////////////////////////////////////////////////////////////////////
template<unsigned int N>
VecN<N> VecN<N>::operator -() const
{
	VecN<N> res;
	for (unsigned int i = 0; i < N; ++i)
	{
		res.data[i] = -data[i];
	}
	return res;
}

//////////////////////////////////////////////////////////////////////////
template<unsigned int N>
VecN<N> VecN<N>::operator *(gdScalar scalar) const
{
	VecN<N> res;
	for (unsigned int i = 0; i < N; ++i)
	{
		res.data[i] = data[i] * scalar;
	}
	return res;
}

//////////////////////////////////////////////////////////////////////////
template<unsigned int N>
VecN<N> VecN<N>::operator /(gdScalar scalar) const
{
	VecN<N> res;
	for (unsigned int i = 0; i < N; ++i)
	{
		res.data[i] = data[i] / scalar;
	}
	return res;
}

//////////////////////////////////////////////////////////////////////////
template<unsigned int N>
gdScalar VecN<N>::dotProduct(const VecN<N>& second) const
{
	gdScalar res = gdScalar(0.0);
	for (unsigned int i = 0; i < N; ++i)
	{
		res += data[i] * second.data[i];
	}
	return res;
}

//////////////////////////////////////////////////////////////////////////
template<unsigned int N>
VecN<N>& VecN<N>::operator +=(const VecN<N>& second)
{
	for (unsigned int i = 0; i < N; ++i)
	{
		data[i] += second.data[i];
	}
	return (*this);
}

//////////////////////////////////////////////////////////////////////////
template<unsigned int N>
VecN<N>& VecN<N>::operator -=(const VecN<N>& second)
{
	for (unsigned int i = 0; i < N; ++i)
	{
		data[i] -= second.data[i];
	}
	return (*this);
}

//////////////////////////////////////////////////////////////////////////
template<unsigned int N>
VecN<N>& VecN<N>::operator *=(gdScalar scalar)
{
	for (unsigned int i = 0; i < N; ++i)
	{
		data[i] *= scalar;
	}
	return (*this);
}

//////////////////////////////////////////////////////////////////////////
template<unsigned int N>
VecN<N>& VecN<N>::operator /=(gdScalar scalar)
{
	for (unsigned int i = 0; i < N; ++i)
	{
		data[i] /= scalar;
	}
	return (*this);
}

//////////////////////////////////////////////////////////////////////////
template<unsigned int N>
gdScalar VecN<N>::lengthSquared() const
{
	gdScalar res = gdScalar(0.0);
	for (unsigned int i = 0; i < N; ++i)
	{
		res += data[i] * data[i];
	}
	return res;
}

//////////////////////////////////////////////////////////////////////////
template<unsigned int N>
gdScalar VecN<N>::length() const
{
	return sqrt(lengthSquared());
}

//////////////////////////////////////////////////////////////////////////
template<unsigned int N>
gdScalar VecN<N>::distance(const VecN<N> &second) const
{
	return sqrt(distanceSquared(second));
}

//////////////////////////////////////////////////////////////////////////
template<unsigned int N>
gdScalar VecN<N>::distanceSquared(const VecN<N> &second) const
{
	gdScalar res = gdScalar(0.0);
	for (unsigned int i = 0; i < N; ++i)
	{
		res += (data[i] - second.data[i]) * (data[i] - second.data[i]);
	}
	return res;
}

//////////////////////////////////////////////////////////////////////////
template<unsigned int N>
void VecN<N>::normalize()
{
	gdScalar len = length();
	_ASSERT(len > gdScalar(0));
	for (unsigned int i = 0; i < N; ++i)
	{
		data[i] /= len;
	}
}

//////////////////////////////////////////////////////////////////////////
template<unsigned int N>
void VecN<N>::setLength(gdScalar newLength)
{
	gdScalar len = length();
	_ASSERT(len > gdScalar(0));
	newLength /= len;
	for (unsigned int i = 0; i < N; ++i)
	{
		data[i] *= newLength;
	}
}

//////////////////////////////////////////////////////////////////////////
template<unsigned int N>
const gdScalar& VecN<N>::operator [](unsigned int index) const
{
	_ASSERT(index < N);
	return data[index];
}

//////////////////////////////////////////////////////////////////////////
template<unsigned int N>
gdScalar& VecN<N>::operator [](unsigned int index)
{
	_ASSERT(index < N);
	return data[index];
}

//////////////////////////////////////////////////////////////////////////
template<unsigned int N>
void VecN<N>::set(gdScalar val)
{
	for (unsigned int i = 0; i < N; ++i)
	{
		data[i] = val;
	}
}

//////////////////////////////////////////////////////////////////////////

#endif // mathVec3_INCLUDED